/*
 * logthread.h
 *
 *  Created on: 2011-5-8
 *      Author: chenl
 */

#ifndef LOGTHREAD_H_
#define LOGTHREAD_H_

#include "common_thread.h"
#include "datetime.h"
#include "logqueue.h"
#include <stdio.h>

typedef struct tagLogFileState
{
	FILE*	pFile;
	int64_t	lastFlushTime;
	int64_t	activeTime;
	char	szLogFileName[enmMaxLogFileNameLen];
}LogFileState;

enum
{
	enmFlushPeriod		= 1000,
	enmWriteLogPeriod 	= 60 * US_PER_SECOND,
};

template<unsigned SOURCE_COUNT = enmMaxLogSource, int THERAD_EXECUTE_PERIOD = enmDefaultThreadExecutePeriod>
class CLogThread : public CThread
{
public:
	CLogThread(CLogQueue<SOURCE_COUNT>* pLogQueue)
	: pLogQueue_(pLogQueue)
	{
	}
	virtual ~CLogThread()
	{
	}

	int32_t Initialize()
	{
		memset(arrLogFileState_, 0, sizeof(arrLogFileState_));
		return NULL == pLogQueue_ ?  E_NULLPOINTER : S_OK;
	}
	int32_t Uninitialize()
	{
		return S_OK;
	}

	virtual void Execute()
	{
		int32_t ret = S_OK;
		bool bIdle = true;
		char szLog[enmMaxLogLen];
		szLog[0] = '\0';
		unsigned len = enmMaxLogLen;
		char szDate[MAX_DATE_LEN + 1] = {0};

		while((!IsTerminated()) || (!bIdle))
		{
			bIdle = true;
			for (unsigned i = 0; i < SOURCE_COUNT; ++i)
			{
				len = enmMaxLogLen;
				ret = ReadLog(i, szDate, szLog, len);
				if (0 <= ret)
				{
					WriteLog(i, szDate, szLog, len);
					bIdle = false;
				}
			}
			//所有隊列中都沒有日誌
			if (bIdle)
			{
				const CDateTime& now = CDateTime::CurrentDateTime();
				for (unsigned i = 0; i < SOURCE_COUNT; ++i)
				{
					if (NULL == arrLogFileState_[i].pFile)
					{
						continue;
					}

					//定期清空IO緩存
					if (now.uSeconds() > arrLogFileState_[i].lastFlushTime + enmFlushPeriod)
					{
						fflush(arrLogFileState_[i].pFile);
					}

					//日誌文件是否需要關閉
					if (now.uSeconds() > arrLogFileState_[i].activeTime + enmWriteLogPeriod)
					{
						fclose(arrLogFileState_[i].pFile);
						memset(&arrLogFileState_[i], 0, sizeof(arrLogFileState_[i]));
					}
				}
				DELAY(THERAD_EXECUTE_PERIOD);
			}
		}
	}

	//從日誌隊列中讀取一條日誌
	int32_t ReadLog(LogSource logSource, char* szDate, char* szLog, uint32_t& len)
	{
		assert(NULL != szDate);
		assert(NULL != szLog);
		if(unlikely(NULL == szDate))
		{
			return E_NULLPOINTER;
		}

		if(unlikely(NULL == szLog))
		{
			return E_NULLPOINTER;
		}

		if (unlikely(0 > logSource || (int)SOURCE_COUNT <= logSource))
		{
			return E_INVALIDARG;
		}
		if (unlikely(enmMaxLogLen < len))
		{
			return E_INVALIDARG;
		}

		//從隊列頭部彈出一條日誌
		--len;	//保留最後'\0'的位置
		int32_t ret = pLogQueue_->Pop(logSource, szLog, len);
		szLog[len] = '\0';
		if (0 > ret)
		{
			return ret;
		}
		//獲取時閒戳
		memcpy(szDate, szLog, MAX_DATE_LEN);
		szDate[MAX_DATE_LEN] = '\0';
		return ret;

	}
	//將日誌寫入日誌文件
	int32_t WriteLog(LogSource logSource, const char* szDate, const char* szLog, unsigned len)
	{
		assert(NULL != szDate);
		assert(NULL != szLog);
		if(unlikely(NULL == szDate))
		{
			return E_NULLPOINTER;
		}

		if(unlikely(NULL == szLog))
		{
			return E_NULLPOINTER;
		}

		if (unlikely(0 > logSource || (int)SOURCE_COUNT <= logSource))
		{
			return E_INVALIDARG;
		}

		//生成日誌文件名
		char *pszLogPrefix = NULL;
		char szLogFileName[enmMaxLogFileNameLen] = {0};
		pszLogPrefix = pLogQueue_->GetLogPrefix(logSource);
		if (NULL == pszLogPrefix)
		{
			return E_UNKNOWN;
		}
		snprintf(szLogFileName, sizeof(szLogFileName), "%s_%s.log", pszLogPrefix, szDate);

		//寫入日誌文件
		const unsigned invalidLogFileStateIndex = SOURCE_COUNT;
		unsigned index = invalidLogFileStateIndex;
		bool bWritten = false;
		//尋找此條日誌的日誌文件
		for (unsigned i = 0; i < SOURCE_COUNT; ++i)
		{
			//日誌文件是否已經打開
			if (NULL == arrLogFileState_[i].pFile)
			{
				//找到首個空閒的日誌序號
				if (invalidLogFileStateIndex == index)
				{
					index = i;
				}
				continue;
			}
			if (0 == strncmp(szLogFileName, arrLogFileState_[i].szLogFileName, enmMaxLogFileNameLen))
			{
				fwrite(szLog, 1, len, arrLogFileState_[i].pFile);
				fwrite("\n", 1, 1, arrLogFileState_[i].pFile);
				bWritten = true;
				//修改文件最後活躍時間
				const CDateTime& now = CDateTime::CurrentDateTime();
				arrLogFileState_[i].activeTime = now.uSeconds();
			}
			else
			{
				//判斷日誌文件是否需要關閉
				const CDateTime& now = CDateTime::CurrentDateTime();
				if (now.uSeconds() > arrLogFileState_[i].activeTime + enmWriteLogPeriod)
				{
					fclose(arrLogFileState_[i].pFile);
					memset(&arrLogFileState_[i], 0, sizeof(arrLogFileState_[i]));
				}
			}
		}
		//日誌文件沒有打開並且還有空位
		if ((!bWritten) && (invalidLogFileStateIndex != index))
		{
			//打開日誌文件
			arrLogFileState_[index].pFile = fopen(szLogFileName, "a+");
			if (NULL != arrLogFileState_[index].pFile)
			{
				const CDateTime& now = CDateTime::CurrentDateTime();
				arrLogFileState_[index].activeTime = now.uSeconds();
				arrLogFileState_[index].lastFlushTime = now.uSeconds();
				snprintf(arrLogFileState_[index].szLogFileName, sizeof(arrLogFileState_[index].szLogFileName), "%s", szLogFileName);
				fwrite(szLog, 1, len, arrLogFileState_[index].pFile);
				fwrite("\n", 1, 1, arrLogFileState_[index].pFile);
			}
		}
		return S_OK;
	}


private:
	LogFileState							arrLogFileState_[SOURCE_COUNT];
	CLogQueue<SOURCE_COUNT, enmMaxLogLen>* 	pLogQueue_;
};

#endif /* LOGTHREAD_H_ */
